#include "LeagueManager.h"
#include "Utilities.h"
#include "GameSettings.h"
#include "League.h"
#include "ZStream.h"
#include "TeamManager.h"
#include "Team.h"
#include "Classification.h"
#include "MatchData.h"
#include "RandomSystem.h"

ZStream& LeagueManagerSettings::Serialize(ZStream& o_stream) const
{
	o_stream.WriteOpenTag("LeagueLayerSettings");

	o_stream.WriteAttribute("LayerNum", m_layerSettings.size());

	for(size_t i = 0; i < m_layerSettings.size(); ++i)
	{
		m_layerSettings[i].Serialize(o_stream);
	}

	o_stream.WriteCloseTag("LeagueLayerSettings");

	return o_stream;
}
ZStream& LeagueManagerSettings::Deserialize(ZStream& i_stream)
{
	m_layerSettings.clear(); //"cagasotto" pattern

	i_stream.SkipLine();

	size_t layerNum = 0;
	i_stream.ReadAttribute(layerNum);

	for(size_t i = 0; i < layerNum; ++i)
	{
		LeagueLayerSettings layerSett;
		
		layerSett.layerIndex = i;
		if(m_layerSettings.empty())
			layerSett.numOfPromoted = 0;
		else
			layerSett.numOfPromoted = m_layerSettings.back().numOfDemotioned;
	
		layerSett.Deserialize(i_stream);

		m_layerSettings.push_back(layerSett);
	}

	i_stream.SkipLine();

	return i_stream;
}

LeagueManager::LeagueManager(ServiceProvider* i_sp)
	: m_currentTurn(0)
	, m_sp(i_sp)
{
	assert(i_sp);
}

LeagueManager::~LeagueManager()
{
	Clear();
}

void LeagueManager::Initialize(const LeagueManagerSettings& i_settings)
{
	Clear();

	m_settings = i_settings;
	m_currentSeason = m_sp->GetSettings().GetValue("I_STARTING_YEAR").Get<size_t>();

	CheckConsistency(i_settings);

	for (size_t i = 0; i < i_settings.m_layerSettings.size(); ++i)
	{
		LeagueLayer* layer = new LeagueLayer(m_sp);
		layer->Initialize(i_settings.m_layerSettings[i]);

		m_layers.push_back(layer);
	}

	for (LayerVecCit it = m_layers.begin(); it != m_layers.end(); ++it)
	{
		LeagueVec leagues = (*it)->GetLeagues();
		Utilities::Append(m_allLeagues, leagues);
		if ((*it)->IsVisible()) 
			Utilities::Append(m_allVisibleLeagues, leagues);
	}

	for (size_t i=0; i<m_allLeagues.size(); ++i)
	{
		m_allLeagues.at(i)->SetID(i);
		TeamVec& teams = m_allLeagues.at(i)->GetTeams();
		for (size_t j=0; j<teams.size(); ++j)
			teams.at(j)->SetLeagueID(i);
	}
}

void LeagueManager::Clear()
{
	Utilities::DeepClear(m_layers);

	m_allLeagues.clear();
	m_allVisibleLeagues.clear();

	m_currentTurn = 0;
}

bool LeagueManager::CheckConsistency(const LeagueManagerSettings &i_settings)
{
	// check consistenza retrocessioni
	LMSettingsCIterator it	= i_settings.m_layerSettings.begin();
	LMSettingsCIterator it2	= it + 1;
	LMSettingsCIterator end	= i_settings.m_layerSettings.end();
	assert(it != end && "No settings available!!!");

	for(; it2 != end; ++it, ++it2)
	{
		size_t MCM = Utilities::GetMCM((*it).numOfLeagues, (*it2).numOfLeagues);
		assert(((*it).numOfDemotioned % MCM) == 0 && "numero squadre retrocesse sbagliato!");
	}

	return true;
}

void LeagueManager::Update(const bool i_doNotPrompt)
{
	for(LayerVecIt it  = m_layers.begin(); it != m_layers.end(); ++it)
	{
		LeagueLayer& theLayer = *(*it);
		theLayer.Update(i_doNotPrompt);
	}

	++m_currentTurn;
}

void LeagueManager::EndSeasonUpdate(const bool i_doNotPrompt, STD::stringstream& o_message)
{
	for (size_t i=0; i<m_allLeagues.size(); ++i)
		m_allLeagues[i]->SortTeams();

	// compute promotions and demotions
	if(GetNumOfLayers(false) > 1)
		for(int i=GetNumOfLayers(false)-1; i>0; --i)
			m_layers[i-1]->PromoteAndDemote(m_layers[i], i_doNotPrompt, o_message);
	
	// every layer must update himself 
	for(LayerVecIt it = m_layers.begin(); it != m_layers.end(); ++it)
		(*it)->EndSeasonUpdate(i_doNotPrompt, o_message);

	++m_currentSeason;

	m_currentTurn = 0;
}

void LeagueManager::HandleBankrupt(Team* const io_bankruptTeam)
{
	// find the league and the layer index  where the team is
	// for all layers from layer index to max layer index:
	//   choose random a league
	//   take the strongest team and promote it to fill the hole
	// when arrived to the last layer, fill the hole with the bankrupted team

	League* teamLeague = m_allLeagues.at(io_bankruptTeam->GetLeagueID());
	teamLeague->RemoveBankruptTeam(io_bankruptTeam);
	for (size_t layer=teamLeague->GetLayerIndex()+1; layer<GetNumOfLayers(false); ++layer)
	{
		const size_t leagueIndex = Rand()%(m_layers[layer]->GetNumOfLeagues());
		League* lowerLeague = m_layers[layer]->GetLeagues().at(leagueIndex);

		TeamVec& teams = lowerLeague->GetTeams();
		Team* promoted = teams.front();
		teams.erase(teams.begin());
		teamLeague->GetTeams().push_back(promoted);
		promoted->SetLeagueID(teamLeague->GetID());

		teamLeague = lowerLeague;
	}

	teamLeague->GetTeams().push_back(io_bankruptTeam);
	io_bankruptTeam->SetLeagueID(teamLeague->GetID());

	// TODO: ... produce some outputs...
}

void LeagueManager::RestartLeagues()
{
	for (size_t i=0; i<m_allLeagues.size(); ++i)
		m_allLeagues[i]->Restart();
}

size_t LeagueManager::GetNumOfTeams(bool onlyVisible) const
{
	const LeagueVec* leagues;
	if (onlyVisible)
		leagues = &m_allVisibleLeagues;
	else
		leagues = &m_allLeagues;

	LeagueVecCit it = leagues->begin();
	LeagueVecCit end = leagues->end();
	size_t counter = 0;
	for (; it != end; ++it)
		counter += (*it)->GetSettings().numOfTeams;
	return counter;
}

size_t LeagueManager::GetNumOfLayers(bool onlyVisible) const
{
	if(onlyVisible)
	{
		size_t ret = 0;
		for(LMSettingsCIterator cit = m_settings.m_layerSettings.begin(); cit != m_settings.m_layerSettings.end(); ++cit)
		{
			if((*cit).isVisible)
				++ret;
		}
		return ret;
	}
	else
		return m_settings.m_layerSettings.size();
}

bool LeagueManager::IsEnded() const
{
	for(LeagueVecCit cit= m_allLeagues.begin(); cit!= m_allLeagues.end(); ++cit)
		if(!(*cit)->IsEnded())
			return false;
	return true;
}

void LeagueManager::GetTeamMatches(const int i_teamID, MatchDataVec& o_matches) const
{
	o_matches.clear();
	const Team* const team = m_sp->GetTeamMgr().GetTeam(i_teamID);
	const League* const league = m_allLeagues.at(team->GetLeagueID());
	const Classification* const classification = league->GetClassification();
	const MatchDataVec& matches = classification->GetMatchesHistory();

	for(MatchDataVecCIt it=matches.begin(); it != matches.end(); ++it)
		if((*it)->IsPlayingTeam(i_teamID))
			o_matches.push_back(*it);
}

ZStream& LeagueManager::Serialize(ZStream& o_stream) const
{
	o_stream.WriteOpenTag("LeagueManager");

	o_stream.WriteAttribute("CurrentSeason", m_currentSeason);
	o_stream.WriteAttribute("CurrentTurn", m_currentTurn);

	for(size_t i = 0; i < m_allLeagues.size(); ++i)
		m_allLeagues[i]->Serialize(o_stream);

	o_stream.WriteCloseTag("LeagueManager");

	return o_stream;
}

ZStream& LeagueManager::Deserialize(ZStream& i_stream)
{
	i_stream.SkipLine();

	i_stream.ReadAttribute(m_currentSeason);
	i_stream.ReadAttribute(m_currentTurn);

	for(size_t i = 0; i < m_allLeagues.size(); ++i)
		m_allLeagues[i]->Deserialize(i_stream);

	i_stream.SkipLine();

	return i_stream;
}